﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.Diagnostics;

using LowLevelGraphics.ColorSpaces;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// This has to be defined somewhere else. ;-)
    /// </summary>
    public enum EdgeDetectMode
    {
        EDGE_DETECT_KIRSH = 1, //Kirsh
        EDGE_DETECT_PREWITT = 2, //Prewitt
        EDGE_DETECT_SOBEL = 3 //Sobel
    }

    /// <summary>
    /// This is the base class for the image filters.
    /// It implements an ifilter interface as also as an icloneable interface
    /// </summary>
    public abstract class BaseImageFilter : IFilter, ICloneable
    {
        protected static Rectangle staticFilterGlobalROI = Rectangle.Empty;
        protected ColorCalculation m_ColorCalculation = null;
        /// <summary>
        /// This might be used to specify
        /// </summary>
        protected PROCESSING_MODE M_PROCESSING_MODE = PROCESSING_MODE.None;

        protected AbstractColorSpace m_AbstractColorSpace = null;

        protected int m_nRectX = -1;
        protected int m_nRectY = -1;
        protected int m_nRectHeight = -1;
        protected int m_nRectWidth = -1;

        /// <summary>
        /// 
        /// </summary>
        /// 
        protected UnsafeBitmap m_UnsafeBitmap = null;

        /// <summary>
        /// This specifies the so called roi.
        /// </summary>
        protected Rectangle m_RectangleROI = Rectangle.Empty;

        /// <summary>
        /// Use this to specify the steps for the filter
        /// implement like this to use:
        /// 
        /// for(int y=0;y smaller nHeight;y+=szStep.Height)
        /// {
        ///     for(int x=0;x smaller nWidth; x+=szStep.Width)
        ///     {
        ///     }
        /// }
        /// </summary>
        protected Size m_szStep = Size.Empty;

        /// <summary>
        /// Specifies the supported pixel formats for this filter. 
        /// </summary>
        protected PixelFormat[] m_SupportedPixelFormats = new PixelFormat[] {
            PixelFormat.Format24bppRgb,
            PixelFormat.Format24bppRgb,
            PixelFormat.Format32bppPArgb
        };

        /// <summary>
        /// This is used to add an offset to the filters calculation
        /// </summary>
        protected float m_fOffset = 0.0f; //Filter offset, this shouldn't be used for geometric scaling

        /// <summary>
        /// This is used to specify a scaling for the filter
        /// </summary>
        protected float m_fScale = 1.0f; //Filter scale, this shouldn't be used for geometric scaling
        
        /// <summary>
        /// 
        /// </summary>
        protected bool m_bClipColor = false; //might be used to clip color range, i.e. if e.g. r>255 r = r % 255; else r>255 then r = 255;

        /// <summary>
        /// Default constructor, used to set global roi if specified.
        /// </summary>
        public BaseImageFilter()
        {
            ApplyRoiIfSet();
        }

        /// <summary>
        /// This will set the global roi to this filter, if one was specified
        /// </summary>
        protected void ApplyRoiIfSet()
        {
            //Apply rectangle if set.
            if (staticFilterGlobalROI != Rectangle.Empty)
            {
                this.m_RectangleROI = staticFilterGlobalROI;
                m_nRectX = this.m_RectangleROI.X;
                m_nRectY = this.m_RectangleROI.Y;
                m_nRectWidth = this.m_RectangleROI.Width;
                m_nRectHeight = this.m_RectangleROI.Height;
            }
        }

        /// <summary>
        /// This is used if a color is meant to clip or not.
        /// 
        /// Clip: r = 255, g = 255, b = 255;
        /// r+5 with clip = r % 255; else if (r>255) r = 255;
        /// </summary>
        public bool ClipColor
        {
            get { return m_bClipColor; }
            set { m_bClipColor = value; }
        }

        /// <summary>
        /// Gets the offset of this filter or sets it
        /// </summary>
        public float Offset
        {
            get { return m_fOffset; }
            set { m_fOffset = value; }
        }

        /// <summary>
        /// Sets or returns the processing mode for a given filter.
        /// </summary>
        public PROCESSING_MODE ProcessingMode
        {
            get { return M_PROCESSING_MODE; }
            set { M_PROCESSING_MODE = value; }
        }

        /// <summary>
        /// Gets or sets the abstract color space.
        /// </summary>
        /// <remarks>This can be used for filters that might be also used with alternative color spaces</remarks>
        /// <value>The abstract color space.</value>
        protected AbstractColorSpace AbstractColorSpace
        {
            get { return m_AbstractColorSpace; }
            set { m_AbstractColorSpace = value; }
        }

        /// <summary>
        /// Gets the scale of this filter or sets it
        /// </summary>
        public float Scale
        {
            get { return m_fScale; }
            set { m_fScale = value; }
        }

        /// <summary>
        /// Filter global roi
        /// If this is set, all filter will get this specified roi
        /// </summary>
        public static Rectangle FilterGlobalRoi
        {
            get { return staticFilterGlobalROI; }
            set { staticFilterGlobalROI = value; }
        }   

        /// <summary>
        /// Supported pixel formats
        /// </summary>
        public virtual PixelFormat[] SupportedPixelFormats
        {
            get { return m_SupportedPixelFormats; }
        }

        /// <summary>
        /// Execute filter on roi
        /// </summary>
        /// <returns></returns>
        public virtual UnsafeBitmap ExecuteROI()
        {
            return ExecuteROI(this.m_RectangleROI);
        }

        /// <summary>
        /// Execute filter on roi
        /// </summary>
        /// <param name="_rectangle"></param>
        /// <returns></returns>
        internal virtual protected UnsafeBitmap ExecuteROI(Rectangle _rectangle)
        {
            return null;
        }

        /// <summary>
        /// if a Bitmap is specified to this filter, use this execution overload
        /// </summary>
        /// <returns></returns>
        public virtual UnsafeBitmap Execute()
        {
            return Execute(this.Bitmap);
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        public virtual UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            Debug.WriteLine(String.Format("Filter: {0}", this.GetType().FullName));
            m_UnsafeBitmap = _bitmap;
            return _bitmap;
        }

        /// <summary>
        /// Apply this to each channel containing a "TRUE"
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="_bIncludeA">if set to <c>true</c> [_b include A].</param>
        /// <param name="_bIncludeR">if set to <c>true</c> [_b include R].</param>
        /// <param name="_bIncludeG">if set to <c>true</c> [_b include G].</param>
        /// <param name="_bIncludeB">if set to <c>true</c> [_b include B].</param>
        public virtual void ExecuteChannelled(UnsafeBitmap _bitmap, bool _bIncludeA, bool _bIncludeR, bool _bIncludeG, bool _bIncludeB)
        {
            UnsafeBitmap _bitmapMask = new UnsafeBitmap(Definitions.White, _bitmap.Size);
            UnsafeBitmap bitmapMask = _bitmapMask;
            ExecutedMaskedChannelled(_bitmap, _bitmapMask, _bIncludeA, _bIncludeR, _bIncludeG, _bIncludeB);
        }

        /// <summary>
        /// Apply this to each pixel which is WHITE
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <param name="_bitmapMask"></param>
        public virtual void ExecutedMasked(UnsafeBitmap _bitmap, UnsafeBitmap _bitmapMask)
        {
            ExecutedMaskedChannelled(_bitmap, _bitmapMask, true, true, true, true);
        }

        /// <summary>
        /// <summary>
        /// Applies a mask.
        /// i.e. the filter will be applied to the whole pic, then it will be applied 
        /// to the original by using the mask.
        /// 
        /// Not very fast, I admit, but easy to implement and it works!!!
        /// </summary>
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <param name="_bitmapMask"></param>
        /// <param name="_bIncludeA"></param>
        /// <param name="_bIncludeR"></param>
        /// <param name="_bIncludeG"></param>
        /// <param name="_bIncludeB"></param>
        public virtual void ExecutedMaskedChannelled(UnsafeBitmap _bitmap, UnsafeBitmap _bitmapMask, bool _bIncludeA, bool _bIncludeR, bool _bIncludeG, bool _bIncludeB)
        {
            UnsafeBitmap _bitmapCloned = ExecuteCloned(_bitmap);
            
            UnsafeBitmap bitmap = _bitmap;
            UnsafeBitmap bitmapCloned = _bitmapCloned;
            UnsafeBitmap bitmapMask = _bitmapMask;

            int nHeight = _bitmap.Height;
            int nWidth = _bitmap.Width;

            for (int y = 0; y < nHeight; y++)
            {
                for (int x = 0; x < nWidth; x++)
                {
                    Color colorMask = bitmapMask.GetPixel(x, y);
                    if (colorMask == Definitions.White)
                    {
                        Color colorCloned = bitmapCloned.GetPixel(x, y);
                        Color colorBitmap = bitmap.GetPixel(x, y);
                        Color colorToSet = Color.FromArgb(
                            _bIncludeA ? colorCloned.A : colorBitmap.A,
                            _bIncludeR ? colorCloned.R : colorBitmap.R,
                            _bIncludeG ? colorCloned.G : colorCloned.G,
                            _bIncludeB ? colorCloned.B : colorBitmap.B);
                        bitmap.SetPixel(x, y, colorToSet);
                    }
                }
            }
            bitmap.Dispose();
            bitmapCloned.Dispose();
            bitmapMask.Dispose();
        }

        /// <summary>
        /// This will first create a copy, and THEN return the changed bitmap only.
        /// The original image stays unchanged.
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public virtual UnsafeBitmap ExecuteCloned(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap bitmap = (UnsafeBitmap)_bitmap.Clone();
            Execute(bitmap);
            return bitmap;
        }


        /// <summary>
        /// Get UnsafeBitmap
        /// </summary>
        public UnsafeBitmap Bitmap
        {
            get { return m_UnsafeBitmap; }
            set { m_UnsafeBitmap = value; }
        }

        #region IFilter Member

        /// <summary>
        /// This will be the Implementation needed for skipping pixels in
        /// the Rectangle property also defined in here.
        /// </summary>
        public Size Step
        {
            get { return m_szStep; }
            set { m_szStep = value; }
        }

        /// <summary>
        /// This is the ROI (region of interest)
        /// </summary>
        public Rectangle Rectangle
        {
            get { return m_RectangleROI; }
            set { m_RectangleROI = value; }
        }

        /// <summary>
        /// Returns the filtername
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.GetType().Name;
        }

        #endregion

        #region ICloneable Members

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public abstract object Clone();
        //{
        //    return null;
        //}

        #endregion
    }
}
